﻿/*
 * @author: wizardc
 */

using System.Collections.Generic;
using Dou.Clip;
using Dou.Job;
using Unity.Burst;
using Unity.Collections;

namespace Demo.WorldMap.Prefab
{
    /// <summary>
    /// 更新 BVH 节点是否可视信息
    /// </summary>
    [BurstCompile]
    public unsafe class NodeVisibleUpdateJob : ManagedJob<NodeVisibleUpdateJob>.IWork
    {
        public SceneStaticCameraCtl cameraCtl;
        public PrefabResContainer container;
        
        public PrefabStaticRenderNode* nodePtr;
        
        public NativeArray<bool> visibleFlagArray;
        
        private Stack<int> _stack = new Stack<int>();

        [BurstCompile]
        [Il2CppSetOption(Option.ArrayBoundsChecks, false)]
        [Il2CppSetOption(Option.NullChecks, false)]
        public void Execute()
        {
            _stack.Push(0);
            while (_stack.Count > 0)
            {
                var nodeIndex = _stack.Pop();
                ref var node = ref nodePtr[nodeIndex];

                var bounds = node.bounds;
                var lastClippingType = node.clippingType;
                var clippingType = cameraCtl.GetClipping(ref bounds);

                // 相交时表示该节点内部的节点可能存在不被包含的情况，需要向下进行判定
                // 被完全包含时子项也是被完全包含的，不需要向下处理
                // 节点状态变化时向下处理，主要用于判定 FrustumClippingType.None 的情况，保证未设定时会向下处理
                if (clippingType == FrustumClippingType.Cross || lastClippingType != clippingType)
                {
                    // 相交的情况
                    if (clippingType == FrustumClippingType.Cross)
                    {
                        if (node.isLeaf)
                        {
                            SetLeafState(nodeIndex, clippingType, lastClippingType);
                        }
                        else
                        {
                            if (node.child1 > -1)
                            {
                                _stack.Push(node.child1);
                            }
                            if (node.child2 > -1)
                            {
                                _stack.Push(node.child2);
                            }
                        }
                    }
                    // 被包含或者不被包含可以直接加入所有子节点
                    else
                    {
                        if (node.isLeaf)
                        {
                            SetLeafState(nodeIndex, clippingType, lastClippingType);
                        }
                        else
                        {
                            // 同样的方式遍历处理完所有子节点
                            var count = _stack.Count;
                            _stack.Push(nodeIndex);
                            while (_stack.Count > count)
                            {
                                nodeIndex = _stack.Pop();
                                node = ref nodePtr[nodeIndex];
                                node.clippingType = clippingType;
                                if (node.child1 > -1)
                                {
                                    _stack.Push(node.child1);
                                }
                                if (node.child2 > -1)
                                {
                                    _stack.Push(node.child2);
                                }
                                if (node.isLeaf)
                                {
                                    SetLeafState(nodeIndex, clippingType, lastClippingType);
                                }
                            }
                        }
                    }
                    
                    node.clippingType = clippingType;
                }
            }

            _stack.Clear();
        }

        [BurstCompile]
        [Il2CppSetOption(Option.ArrayBoundsChecks, false)]
        [Il2CppSetOption(Option.NullChecks, false)]
        private void SetLeafState(int nodeIndex, FrustumClippingType clippingType, FrustumClippingType lastClippingType)
        {
            var visible = visibleFlagArray[nodeIndex];
            switch (clippingType)
            {
                case FrustumClippingType.Inner:
                case FrustumClippingType.Cross:
                    if (!visible) // 是未显示时添加显示
                    {
                        container.AddObj(nodeIndex);
                        visibleFlagArray[nodeIndex] = true;
                    }
                    break;
                case FrustumClippingType.Out:
                    if (visible) // 已经显示时移除
                    {
                        container.RemoveObj(nodeIndex);
                        visibleFlagArray[nodeIndex] = false;
                    }
                    break;
            }
        }
    }
}
